const fs = require("fs");
const fse = require("fs-extra");
const jwt = require("jsonwebtoken");
const schedule = require("node-schedule");
const _ = require("lodash");
const axios = require("axios");
const assert = require("assert");
const path = require("path");
const crypto = require("crypto");
const querystring = require("querystring");
const url = require("url");
const _util = require("util");
const stringDecoder = require("string_decoder");
const buffer = require("buffer");
const http = require("http");
const https = require("https");
const ajv = require("ajv");
const bcrypt = require("bcryptjs");
const xml2js = require("xml2js");
const bluebird = require("bluebird");
const jsonwebtoken = require("jsonwebtoken");
const md5 = require("md5");
const moment = require("moment");
const uuid = require("uuid");
const qiniu = require("qiniu");
const nodemailer = require("nodemailer");
const wechat = require("co-wechat");
const wechatPay = require("wechat-pay");
const wechatCrypto = require("wechat-crypto");
const WechatApi = require("co-wechat-api");
const WechatOauth = require("co-wechat-oauth");
const WechatOpenApi = require("co-wechat-open-api");
const WxaApi = require("weapp-api");
const WxaOpenApi = require("weapp-open-api");
const aliPay = require("easy-alipay");
const cheerio = require("cheerio");
const plugin = require("./plugin");

// vm sandbox
const sandbox = {
  ajv,
  axios,
  bcrypt,
  bluebird,
  jsonwebtoken,
  jwt: jsonwebtoken,
  lodash: _,
  _,
  md5,
  moment,
  uuid,
  qiniu,
  nodemailer,
  wechat,
  wechatPay,
  wechatCrypto,
  WechatApi,
  WechatOauth,
  WechatOpenApi,
  WxaApi,
  WxaOpenApi,
  aliPay,
  cheerio,
  xml2js,
  assert,
  path,
  crypto,
  querystring,
  url,
  util: _util,
  string_decoder: stringDecoder,
  buffer,
  http,
  https
};

module.exports = (ctx, next) => {
  // redis缓存默认值是24小时
  const redis = {
    lock: (_key, ...argv) => {
      return BaaS.redis.lock(ctx.getAppKey(_key, true), ...argv);
    },
    unlock: (_key, ...argv) => {
      return BaaS.redis.unlock(ctx.getAppKey(_key, true), ...argv);
    },
    delAll: (_key, ...argv) => {
      return BaaS.redis.delAll(ctx.getAppKey(_key, true), ...argv);
    },
    cache: (_key, ...argv) => {
      return BaaS.redis.cache(ctx.getAppKey(_key, true), ...argv);
    }
  };
  for (const key in BaaS.redis) {
    if (_.endsWith(key, "Async")) {
      redis[key.substr(0, key.length - 5)] = (_key, ...argv) => {
        return BaaS.redis[key](ctx.getAppKey(_key, true), ...argv).then(res => {
          return BaaS.redis
            .expireAsync(ctx.getAppKey(_key, true), 3600 * 24)
            .then(() => {
              return res;
            });
        });
      };
    }
  }

  return Object.assign(
    {
      module: Object.assign(
        sandbox,
        {
          http: Object.assign({}, http, {
            createServer() {
              throw new Error("Access denied to http.createServer");
            }
          }),
          https: Object.assign({}, https, {
            createServer() {
              throw new Error("Access denied to https.createServer");
            }
          })
        },
        {
          fse: {
            copy: (src, dest, options) => {
              return fse.copy(ctx.getAppDir(src), ctx.getAppDir(dest), options);
            },
            emptyDir: dir => {
              return fse.emptyDir(ctx.getAppDir(dir));
            },
            ensureFile: file => {
              return fse.ensureFile(ctx.getAppDir(file));
            },
            ensureDir: dir => {
              return fse.ensureDir(ctx.getAppDir(dir));
            },
            ensureLink: (srcpath, dstpath) => {
              return fse.ensureLink(
                ctx.getAppDir(srcpath),
                ctx.getAppDir(dstpath)
              );
            },
            ensureSymlink: (srcpath, dstpath) => {
              return fse.ensureSymlink(
                ctx.getAppDir(srcpath),
                ctx.getAppDir(dstpath)
              );
            },
            mkdirs: dir => {
              return fse.mkdirs(ctx.getAppDir(dir));
            },
            move: (src, dest, options) => {
              return fse.move(ctx.getAppDir(src), ctx.getAppDir(dest), options);
            },
            outputFile: (file, data, options) => {
              return fse.outputFile(ctx.getAppDir(file), data, options);
            },
            outputJson: (file, object, options) => {
              return fse.outputJson(ctx.getAppDir(file), object, options);
            },
            pathExists: file => {
              return fse.pathExists(ctx.getAppDir(file));
            },
            readFile: file => {
              return new Promise((resolve, reject) => {
                fs.readFile(ctx.getAppDir(file), (err, data) => {
                  if (err) reject(err);
                  resolve(data);
                });
              });
            },
            statFile: file => {
              return new Promise((resolve, reject) => {
                fs.stat(ctx.getAppDir(file), (err, data) => {
                  if (err) reject(err);
                  resolve(data);
                });
              });
            },
            readJson: (file, options) => {
              return fse.readJson(ctx.getAppDir(file), options);
            },
            remove: path => {
              return fse.remove(ctx.getAppDir(path));
            },
            writeJson: (file, object, options) => {
              return fse.writeJson(ctx.getAppDir(file), object, options);
            }
          },
          redis
        },
        plugin(ctx, next)
      )
    },
    {
      ctx,
      next,
      console
    }
  );
};
